(function($) {
	$.fn.tablefilter = function ()
	{
		var originalTableCols = new Array();
		var originalTableRows = new Array();
		var pivotColumns = new Array();
		var pivotValues = new Array();
		var usedFilters = new Array();
		var usedValues = new Array();
	    var headers = new Array();
	    var showFilter = new Array();
	    var $table = false;
 
	    /***********
	     * Build the tablefilters
	     */
	    return this.each(function() 
	    {
	        $table = $(this);
	        
	        // headers
	        $('thead', $table).each(function(row){
	        	$('th', row).each(function(column) {
		        	headers.push(this);
		        });    
	        });

	        // set class
	        var counter = 0;
	        $('tr', $table).each(function(cell){
				if(counter%2==0)
					this.className = 'odd';
				counter++;
	        });
	        
	        // store original Table
	        $('td', $table).each(function(cell){
	            var column = $(this).parent().children().index(this);
	            var row = $(this).parent().parent().children().index(this.parentNode);
	            
				try{
					originalTableRows[row][column] = this;
				}catch(err){
					originalTableRows[row] = new Array();
					originalTableRows[row][column] = this;
				}
				
				try{
					originalTableCols[column][row] = this;
				}catch(err){
					originalTableCols[column] = new Array();
					originalTableCols[column][row] = this;
				}
	        });

	        // get Filters
			for(var col=0; col < originalTableCols.length;col++)
			{
				// check if second value is in the column more than twice
				if(countInnerHTMLInArray(originalTableCols[col][2],originalTableCols[col])>2)
				{
					pivotColumns.push(col);
				}
			}				

			// get all unique values for the filters
			for(var i=0; i<pivotColumns.length;i++)
			{
				var col = pivotColumns[i];
				pivotValues[col] = new Array();
				showFilter[col] = new Array();
				for(var row = 0; row < originalTableCols[col].length; row++)
				{
					// check vor each row in this column if this value is unique
					if(countValueInArray(getNodeText(originalTableCols[col][row]),pivotValues[col])==0)
					{
						pivotValues[col].push(getNodeText(originalTableCols[col][row]));
						showFilter[col].push(true);
					}
				}
			}
			
			// append filters to the page.
			for(var i=0; i<pivotColumns.length;i++)
			{
				var col = pivotColumns[i];				
				var selectBox = '<select class="pivotFilter" id="'+col+'">';
				// first option is the column name
				selectBox = selectBox + '<option value="remove">'+getNodeText(headers[col])+'</option>';
				for(var x = 0; x < pivotValues[col].length; x++)
				{
					selectBox = selectBox + '<option value="'+x+'">'+pivotValues[col][x]+'</option>';
				}
				selectBox = selectBox + '</select>';
				$("#selectfilter").append(selectBox);
			}
			$selectie = $("#selectfilter");
			$('select', $selectie).bind("change", function()
			{
				var filter = $(this).attr("id");
				var value = $(this).val();
				// if this row is already a filter we need te rest that
				for(var x=0; x<usedFilters.length;x++)
				{
					if(usedFilters[x] == filter)
					{
						// update selectie (select again)
						for(var v=0;v<showFilter[x].length;v++)
						{
							showFilter[filter][v]=true;
						}
						usedFilters.splice(x, 1);
						usedValues.splice(x, 1);
					}
				}
				// now add the filter...
				if(
					value != 'remove' 
					&& !isNaN(value)
				)
				{	// add filters
					if(pivotValues[filter].length >= parseInt(value))
					{
						usedFilters.push(filter);
						usedValues.push(value);
						// update selectie (deselect again)
						for(var v=0;v<showFilter[filter].length;v++)
						{
							showFilter[filter][v] = false;
						}
						showFilter[filter][value] = true;
					}
				}			
				rebuildTable();
			});
			
			// onclick function to pivotHeaders
			$('thead', $table).each(function(row) 
			{
	        	$('th', row).each(function(column) 
	        	{
	    			for(var i=0; i<pivotColumns.length;i++)
	    			{
		    			var col = pivotColumns[i];
		    			if(getNodeText(this) == getNodeText(headers[col]))
		    			{
			    			this.className = 'clickable';				
		    				// first option is the column name
		    				var id = getNodeText(headers[col]);
		    				var appendText = '';
		    				appendText += '<div class="options" id="'+id+'" >';
		    				//appendText += '<b>'+getNodeText(headers[col])+'</b>:<br />';
		    				for(var x = 0; x < pivotValues[col].length; x++)
		    				{
		    					var thisid = getNodeText(headers[col])+'_'+pivotValues[col][x];
		    					appendText += '<input type="checkbox" id="'+thisid+'" col="'+col+'" value="'+x+'" checked="checked" / >';
		    					appendText += '<label for="'+thisid+'">'+pivotValues[col][x]+'</label>';
		    					appendText += '<br />';
		    				}
		    				appendText += '</div>';
		    				$(this).append(appendText);
		    				id = '#'+id;
			        		$(this).click(function()
			        		{
				    			$(id).toggle();
				        	});
		    			}
	    			}
	        	});
			});
			// bind option click function
			$options = $(".options");
			$('input:checkbox', $options).bind("click", function()
			{
				var filter = $(this).attr("col"), 
					value = $(this).val(), 
					checked = $(this).attr("checked");
				showFilter[filter][value]=checked;
				rebuildTable();
			});
			
			rebuildTable();
	    });
		
		/************
		 * Private functions 
		 */

		function clearTable()
		{
			$('tbody', $table).each(function(row){
				this.innerHTML = '';
			});
		}
		
		function rebuildTable()
		{
			clearTable();
	
			// check checkboxes
			for(var filter=0;filter<showFilter.length;filter++)
			{
				for(var value=0;value<showFilter[filter].length;value++)
				{	
					var thisid = '#'+getNodeText(headers[filter])+'_'+pivotValues[filter][value];
					//alert(thisid);
					$(thisid).attr('checked', showFilter[filter][value])
					// $(thisid).checked = showFilter[filter][value];
				}
			}
			// rebuild the table
			var Totaal = new Array();
			// reset totaal
			for(var cell=0; cell<originalTableCols.length; cell++)
			{
				Totaal[cell] = 0;
			}
	
			var usedTableRows = originalTableRows;
			
			
			//now use the filters to select the correct rows
			for(var fi=0; fi<usedFilters.length;fi++)
			{
				var filter = usedFilters[fi];
				var value = usedValues[fi]; 
				var search = pivotValues[filter][value]; 
				var showTableRows = new Array();
				for(var row=0; row<usedTableRows.length; row++)
				{
					if(getNodeText(usedTableRows[row][filter]) == search)
					{
						showTableRows.push(usedTableRows[row]);
					}
				}
				usedTableRows = showTableRows;
			}
			// and we use the selects to filter the rows
			for(var fi=0; fi<showFilter.length; fi++)
			{
				var filter = showFilter[fi];
				for(var vi=0; vi<showFilter[fi].length; vi++)
				{
					if(!showFilter[fi][vi])
					{	// hide these rows 
						var search = pivotValues[fi][vi];
						var showTableRows = new Array();
						for(var row=0; row<usedTableRows.length; row++)
						{
							if(getNodeText(usedTableRows[row][fi]) != search)
							{
								showTableRows.push(usedTableRows[row]);
							}
						}
						usedTableRows = showTableRows;
					}
				}
			}
			
			// display the result
			var counter = 0;
			if(usedTableRows.length > 0)
			{
				for(var row = 0; row<usedTableRows.length;row++)
				{	
					if(counter%2==0)
						var appendRow = '<tr>';
					else
						var appendRow = '<tr class="odd">';
					counter++;
					for(var cell=0; cell<usedTableRows[row].length; cell++)
					{
						appendRow += '<td>'+usedTableRows[row][cell].innerHTML+'</td>';
						var cellValue = parseFloat(getNodeText(usedTableRows[row][cell]));	
						Totaal[cell] += (isNaN(cellValue)?0:cellValue); 
					}
					appendRow += '</tr>';
					$('tbody', $table).append(appendRow);
				}
			}
			else
			{	// to many filters.. no result
				// show the used filters to the user and tell hem this aint going to work
				var alertText = 'Er zijn geen resultaten met deze Filters:\n';
				for(var x=0; x<usedFilters.length;x++)
				{
					var filter = getNodeText(headers[usedFilters[x]]);
					var value = pivotValues[usedFilters[x]][usedValues[x]];
					alertText += filter+' = '+value+'\n';
				}
				alertText += '\nen de volgende vinkjes:\n';
				for(var x=0; x<showFilter.length;x++)
				{
					var filter = getNodeText(headers[x]);
					for(var y=0; y<showFilter[x].length;y++)
					{
						if(showFilter[x][y])
						{
							var value = pivotValues[x][y];
							alertText += filter+' = '+value+'\n';
						}
					}
				}
				alert(alertText);
			}
			
			// totaal
			if(counter%2==0)
				var appendRow = '<tr>';
			else
				var appendRow = '<tr class="odd">';
			for(cell=0; cell<Totaal.length;cell++)
			{
				if(cell == filter)
				{
					appendRow += '<td><b>Totaal</b></td>';
				}
				else if(countValueInArray(cell,pivotColumns)>=1)
				{	// pivotColumn we exclude from total values
					
					appendRow += '<td></td>';
				}	
				else
				{
					appendRow += '<td>'+(Math.round(Totaal[cell]*100)/100)+'</td>';
				}	
			}
			appendRow += '</tr>';
			$('tbody', $table).append(appendRow);
		}
	
		function getNodeText(node)
		{
			if(!node) return "";
			var t="";
			if(node.childNodes[0] && node.childNodes[0].hasChildNodes())
			{
				t = getNodeText(node.childNodes[0]);
			}
			else
			{
				t = trim(node.innerHTML);
				if(t.indexOf('<')>0)
				{
					t = t.substr(0, t.indexOf('<'));
				}
			}
			return t;
		}
	
		function countInnerHTMLInArray(value, array)
		{
			var lenght=array.length,
				retVal = 0,
				value = getNodeText(value);
			for(var i=0;i<lenght;i++)
			{
				if(getNodeText(array[i]) == value)
				{
					retVal++;
				}
			}
			return retVal;
			
		}
		
		function countValueInArray(v,a)
		{
			var l=a.length,
				retVal = 0;
			for(var i=0;i<l;i++)
			{
				if(a[i] == v)
				{
					retVal++;
				}
			}
			return retVal;
		}
	
		function trim(value) 
		{
			value = value.replace(/^\s+/,'');
			value = value.replace(/\s+$/,'');
			return value;
		}
	};
 })(jQuery);